/**
 * Copyright (C) 2010 Sergey Grigoriev <s.a.grigoriev@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.gmf.core

import org.apache.maven.plugin.MojoFailureException
import org.apache.commons.lang.StringUtils
import org.apache.log4j.Logger
import com.google.inject.Inject

/**
 * Executable available in operating system path
 */
class OSCommand {

  @Inject
  Logger l4j

  /**
   * Text produced by standard output stream
   */
  def stdout

  def opts = []
  String name

  /**
   * The process will be launched in this folder
   */
  def final baseDir

  def OSCommand(String name, File baseDir, String prefix = null) {
    this.name = StringUtils.isBlank(prefix) ? name : [prefix, name].join(File.separator)
    this.baseDir = baseDir;
  }

  /**
   * Append options or arguments to the command line
   * @param newOptions map of options, list of arguments,  or a single option or argument. 
   * 	    Examples:  cmd << '-compiler.debug'
   * 		       cmd << ['-compiler.library.path+=':['lib1','lib2'], '-compiler.source-path=':'src']
   * 		       cmd << ['-compiler.headless-server' ,'Main.mxml']
   */
  def leftShift(newOptions) {

    if (newOptions instanceof Map) {
      opts << newOptions
    } else if (newOptions instanceof List) {
      newOptions.each { opts << it}
    } else if (newOptions instanceof CharSequence) {
      opts << newOptions
    } else {
      throw new IllegalArgumentException()
    }
  }

  List<String> processOptions() {
    def result = []
    opts.each { opt ->
      if (opt instanceof Map) {
        result += opt.collect() { key, value ->
          def optionString = new StringBuilder()
          optionString << key
          if (value instanceof List) {
            optionString << value.join(',')
          } else {
            optionString << value
          }
          return optionString.toString()
        }
      } else {
        result << opt
      }
    }

    result.sort { String left, String right ->
      if (left.startsWith('-')) {
        return -1;
      } else if (right.startsWith('-')) {
        return 1;
      } else {
        return 0;
      }
    }
    result.add(0, adjustExecutableName())
    result.collect {it.toString()}   // Convert GStrings to java.lang.String and return
  }

  def run() {
    def optionsList = processOptions()
    def logMessage = optionsList.join(' ')
    l4j.info(logMessage)

    ProcessBuilder pb = new ProcessBuilder(optionsList)
    pb.directory(baseDir)
    Process proc = pb.start()
    ProcessStreamReader reader = new ProcessStreamReader(proc)
    reader.waitFor()
    
    l4j.info(reader.stdOut)
    if (reader.stdErr) {
      l4j.warn(reader.stdErr)
    }
    proc.waitFor()
    if (proc.exitValue() != 0) {
      throw new MojoFailureException(proc.err.text)
    }

    stdout = reader.stdOut
  }

  def private adjustExecutableName() {
    def result = name
    if (System.properties['os.name'].toLowerCase().contains('windows')) {
      def candidates = ["${name}.exe", "${name}.bat"]
      candidates.each {
        if (new File(it).exists()) {
          result = it
        }
      }
    }
    return result
  }
}
